package com.example.wecomservice.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.example.wecomservice.common.CacheManager;
import com.example.wecomservice.common.contacts.CacheKey;
import com.example.wecomservice.common.exception.BusinessException;
import com.example.wecomservice.pojo.dto.*;
import com.example.wecomservice.service.IWeComService;
import com.example.wecomservice.utils.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author Jamin
 * @Date 2023/2/3 11:06
 */
@Slf4j
@Service
public class WeComServiceImpl implements IWeComService {

    @Autowired
    @Qualifier("restTemplate")
    private RestTemplate restTemplate;

    @Value("${weCom.corpId}")
    private String corpId;
    @Value("${weCom.suite.suiteId}")
    private String suiteId;
    @Value("${weCom.suite.secret}")
    private String suiteSecret;
    @Value("${weCom.suite.token}")
    private String suiteToken;
    @Value("${weCom.suite.encodingAESKey}")
    private String suiteEncodingAESKey;

    private String weCom_suite_accessToken = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";
    private String weCom_corp_accessToken = "https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=";
    private String weCom_permanent_accessToken = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=";
    private String weCom_oauth_userinfo = "https://qyapi.weixin.qq.com/cgi-bin/service/auth/getuserinfo3rd";

    @Override
    public AccessTokenDto getSuiteAccessToken(String suiteTicket) {
        SuiteTokenDto suiteTokenDto = new SuiteTokenDto();
        suiteTokenDto.setSuite_id(suiteId);
        suiteTokenDto.setSuite_secret(suiteSecret);
        suiteTokenDto.setSuite_ticket(suiteTicket);
        String jsonStr = restTemplate.postForObject(weCom_suite_accessToken, suiteTokenDto, String.class);
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        if (!requestOk(jsonObject)) {
            throw new BusinessException("应用凭证获取失败，" + jsonObject.getString("errmsg"));
        }
        AccessTokenDto accessTokenDto = new AccessTokenDto();
        accessTokenDto.setAccessToken(jsonObject.getString("suite_access_token"));
        accessTokenDto.setExpiresIn(jsonObject.getString("expires_in"));
        return accessTokenDto;
    }

    /**
     * 验证URL
     *
     * @param msgSignature 签名串，对应URL参数的msg_signature
     * @param timeStamp    时间戳，对应URL参数的timestamp
     * @param nonce        随机串，对应URL参数的nonce
     * @param echoStr      随机串，对应URL参数的echostr
     * @return 解密之后的echostr
     */
    @Override
    public String weComVerify(String msgSignature, String timeStamp, String nonce, String echoStr) {
        String signature = AESUtil.getSHA1(suiteToken, timeStamp, nonce, echoStr);

        if (!signature.equals(msgSignature)) {
            log.error("企业微信签名验证错误");
            return null;
        }

        String result = AESUtil.weComDecrypt(echoStr, suiteEncodingAESKey, corpId);
        return result;
    }

    @Override
    public String weComCommand(String msgSignature, String timeStamp, String nonce, InputStream in) {
        ReceiveDto receive = new ReceiveDto();
        receive.setReceiveId(suiteId);
        receive.setToken(suiteToken);
        receive.setEncodingAesKey(suiteEncodingAESKey);
        try {
            String postData = "";   // 密文，对应POST请求的数据
            //1.获取加密的请求消息：使用输入流获得加密请求消息postData
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String tempStr = "";   //作为输出字符串的临时串，用于判断是否读取完毕
            while (null != (tempStr = reader.readLine())) {
                postData += tempStr;
            }

            String msg = weComDecryptMsg(msgSignature, timeStamp, nonce, postData, receive);
            JSONObject json = JSONObject.parseObject(msg);
            String suiteTicket = json.getString("SuiteTicket");
            String authCode = json.getString("AuthCode"); // 临时授权码,用于获取企业的永久授权码
            // Todo suiteToken存放到缓存
            if (suiteTicket != null) {
                CacheManager.set(CacheKey.Cache_WeCom_Suite_Ticket, suiteTicket, CacheKey.t30min);
            } else {
                suiteTicket = CacheManager.get(CacheKey.Cache_WeCom_Suite_Ticket);
            }
            String suiteAccessToken = CacheManager.get(CacheKey.Cache_WeCom_Suite_Access_Token);
            if (suiteAccessToken == null) {
                AccessTokenDto tokenDto = getSuiteAccessToken(suiteTicket);
                suiteAccessToken = tokenDto.getAccessToken();
                CacheManager.set(CacheKey.Cache_WeCom_Suite_Access_Token, suiteAccessToken, CacheKey.t1hour);
            }
            if (authCode != null) {
                savePermanentCode(authCode, suiteAccessToken);
            }
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String weComDecryptMsg(String msgSignature, String timeStamp, String nonce, String postData, ReceiveDto receive) {
        // 提取密文
        Object[] encrypt;
        if (postData.contains("<xml>")) {
            encrypt = AESUtil.extractXML(postData);
        } else {
            encrypt = AESUtil.extractJSON(postData);
        }

        // 验证安全签名
        String signature = AESUtil.getSHA1(receive.getToken(), timeStamp, nonce, encrypt[1].toString());
        if (!signature.equals(msgSignature)) {
            log.error("签名验证错误");
            return null;
        }

        // 解密
        String result = AESUtil.weComDecrypt(encrypt[1].toString(), receive.getEncodingAesKey(), receive.getReceiveId());
        return result;
    }

    @Override
    public OauthDto oauth(String code, String state) {
        String suiteAccessToken = CacheManager.get(CacheKey.Cache_WeCom_Suite_Access_Token);
        if (suiteAccessToken == null) {
            String suiteTicket = CacheManager.get(CacheKey.Cache_WeCom_Suite_Ticket);
            AccessTokenDto tokenDto = getSuiteAccessToken(suiteTicket);
            suiteAccessToken = tokenDto.getAccessToken();
            CacheManager.set(CacheKey.Cache_WeCom_Suite_Access_Token, suiteAccessToken, CacheKey.t1hour);
        }

        String userInfoStr = getWeComUserInfo(code, suiteAccessToken);
        JSONObject jsonObject = JSONObject.parseObject(userInfoStr);
        String corpid = jsonObject.getString("corpid");
        String openUserid = jsonObject.getString("open_userid");

        OauthDto oauthDto = new OauthDto();
        oauthDto.setOpenUserId(openUserid);
        oauthDto.setAuthCorpId(corpid);
        return oauthDto;
    }

    @Override
    public String getWeComUserInfo(String code, String suiteAccessToken) {
        String url = weCom_oauth_userinfo + "?suite_access_token=" + suiteAccessToken + "&code=" + code;
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        return responseEntity.getBody();
    }

    @Override
    public String getAccessToken(String authCorpId) {
        String accessToken = CacheManager.get(CacheKey.Cache_WeCom_Access_Token + authCorpId);
        if (accessToken == null) {
            // todo 缓存中没有，先到数据库中拿，数据库中的已过期，则通过永久码permanentCode，应用凭证suiteAccessToken获取
            if (accessToken != null) {
                CacheManager.set(CacheKey.Cache_WeCom_Access_Token + authCorpId, accessToken, CacheKey.t1hour); // 新获取到的accessToken有效期是2小时
            }
        }
        return accessToken;
    }

    private boolean requestOk(JSONObject jsonObject) {
        if (!jsonObject.containsKey("errcode")) { // 没返回errcode视为调用成功
            return true;
        }
        boolean bool = jsonObject.getString("errcode").equals("0");
        if (!bool) {
            log.error(jsonObject.getString("errmsg"));
        }
        return bool;
    }

    // 保存永久码
    private void savePermanentCode(String authCode, String suiteAccessToken) {
        CacheManager.set(CacheKey.Cache_WeCom_Auth_Code + suiteId, authCode, CacheKey.t5min);
        String permanentCodeStr = getPermanentCode(authCode, suiteAccessToken);
        JSONObject permanentCodeJson = JSONObject.parseObject(permanentCodeStr);
        String permanentCode = permanentCodeJson.getString("permanent_code");
        String corpInfoStr = permanentCodeJson.getString("auth_corp_info");
        JSONObject corpInfoJson = JSONObject.parseObject(corpInfoStr);
        String authCorpId = corpInfoJson.getString("corpid");
        // 这里将永久码保存在缓存中30天，真正操作应该持久化保存
        CacheManager.set(CacheKey.Cache_WeCom_PermanentCode + authCorpId, permanentCode, CacheKey.t1Month);
        // todo 将企业永久授权码持久化
    }

    /**
     * 获取企业永久授权码
     * authCode 临时授权码
     */
    private String getPermanentCode(String authCode, String suiteToken) {
        PermanentCodeDto permanentCodeDto = new PermanentCodeDto();
        permanentCodeDto.setAuth_code(authCode);
        String url = weCom_permanent_accessToken + suiteToken;
        return restTemplate.postForObject(url, permanentCodeDto, String.class);
    }

}
